From 91e612aea0725c21a814c10f46042bd1707aa4b6 Mon Sep 17 00:00:00 2001 From: "kaf24@firebug.cl.cam.ac.uk" Date: Wed, 15 Feb 2006 00:15:11 +0100 Subject: [PATCH] More fixes to HVM FPU management. Mostly for SVM, but also fix VMX mov_to_cr0: must setup_fpu() if the new cr0 value has TS clear, regardless of previous cr0.TS value. Signed-off-by: Keir Fraser --- xen/arch/x86/hvm/svm/svm.c | 44 +++++++++++++++-------------- xen/arch/x86/hvm/svm/vmcb.c | 6 ++-- xen/arch/x86/hvm/svm/x86_32/exits.S | 2 +- xen/arch/x86/hvm/vmx/vmx.c | 18 ++++-------- xen/arch/x86/traps.c | 3 -- xen/include/asm-x86/i387.h | 1 + 6 files changed, 35 insertions(+), 39 deletions(-) diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c index 830d03921a..14f04bd0a8 100644 --- a/xen/arch/x86/hvm/svm/svm.c +++ b/xen/arch/x86/hvm/svm/svm.c @@ -701,12 +701,21 @@ void svm_stts(struct vcpu *v) { struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; - ASSERT(vmcb); - - vmcb->cr0 |= X86_CR0_TS; + /* FPU state already dirty? Then no need to setup_fpu() lazily. */ + if ( test_bit(_VCPUF_fpu_dirtied, &v->vcpu_flags) ) + return; - if (!(v->arch.hvm_svm.cpu_shadow_cr0 & X86_CR0_TS)) + /* + * If the guest does not have TS enabled then we must cause and handle an + * exception on first use of the FPU. If the guest *does* have TS enabled + * then this is not necessary: no FPU activity can occur until the guest + * clears CR0.TS, and we will initialise the FPU when that happens. + */ + if ( !(v->arch.hvm_svm.cpu_shadow_cr0 & X86_CR0_TS) ) + { v->arch.hvm_svm.vmcb->exception_intercepts |= EXCEPTION_BITMAP_NM; + vmcb->cr0 |= X86_CR0_TS; + } } static void arch_svm_do_launch(struct vcpu *v) @@ -884,14 +893,11 @@ static void svm_do_no_device_fault(struct vmcb_struct *vmcb) { struct vcpu *v = current; - clts(); - setup_fpu(v); + vmcb->exception_intercepts &= ~EXCEPTION_BITMAP_NM; - if (!(v->arch.hvm_svm.cpu_shadow_cr0 & X86_CR0_TS)) + if ( !(v->arch.hvm_svm.cpu_shadow_cr0 & X86_CR0_TS) ) vmcb->cr0 &= ~X86_CR0_TS; - - vmcb->exception_intercepts &= ~EXCEPTION_BITMAP_NM; } @@ -1350,10 +1356,11 @@ static int svm_set_cr0(unsigned long value) vmcb->cr0 = value | X86_CR0_PG; v->arch.hvm_svm.cpu_shadow_cr0 = value; - /* Check if FP Unit Trap need to be on */ - if (value & X86_CR0_TS) - { - vmcb->exception_intercepts |= EXCEPTION_BITMAP_NM; + /* TS cleared? Then initialise FPU now. */ + if ( !(value & X86_CR0_TS) ) + { + setup_fpu(v); + vmcb->exception_intercepts &= ~EXCEPTION_BITMAP_NM; } HVM_DBG_LOG(DBG_LEVEL_VMMU, "Update CR0 value = %lx\n", value); @@ -1669,11 +1676,11 @@ static int svm_cr_access(struct vcpu *v, unsigned int cr, unsigned int type, break; case INSTR_CLTS: - clts(); + /* TS being cleared means that it's time to restore fpu state. */ setup_fpu(current); + vmcb->exception_intercepts &= ~EXCEPTION_BITMAP_NM; vmcb->cr0 &= ~X86_CR0_TS; /* clear TS */ v->arch.hvm_svm.cpu_shadow_cr0 &= ~X86_CR0_TS; /* clear TS */ - vmcb->exception_intercepts &= ~EXCEPTION_BITMAP_NM; break; case INSTR_LMSW: @@ -1803,11 +1810,8 @@ static inline void svm_vmexit_do_hlt(struct vmcb_struct *vmcb) struct vcpu *v = current; struct hvm_virpit *vpit = &v->domain->arch.hvm_domain.vpit; s_time_t next_pit = -1, next_wakeup; - unsigned int inst_len; - svm_stts(v); - inst_len = __get_instruction_length(vmcb, INSTR_HLT, NULL); - __update_guest_eip(vmcb, inst_len); + __update_guest_eip(vmcb, 1); if ( !v->vcpu_id ) next_pit = get_pit_scheduled(v, vpit); @@ -1822,7 +1826,6 @@ static inline void svm_vmexit_do_hlt(struct vmcb_struct *vmcb) static inline void svm_vmexit_do_mwait(void) { - return; } @@ -2494,7 +2497,6 @@ asmlinkage void svm_vmexit_handler(struct cpu_user_regs regs) break; case VMEXIT_INTR: - svm_stts(v); raise_softirq(SCHEDULE_SOFTIRQ); break; diff --git a/xen/arch/x86/hvm/svm/vmcb.c b/xen/arch/x86/hvm/svm/vmcb.c index 5d45a4c25e..51253ee666 100644 --- a/xen/arch/x86/hvm/svm/vmcb.c +++ b/xen/arch/x86/hvm/svm/vmcb.c @@ -489,7 +489,9 @@ void svm_do_resume(struct vcpu *v) { struct domain *d = v->domain; struct hvm_virpit *vpit = &d->arch.hvm_domain.vpit; - + + svm_stts(v); + if ( test_bit(iopacket_port(d), &d->shared_info->evtchn_pending[0]) || test_bit(ARCH_HVM_IO_WAIT, &v->arch.hvm_vcpu.ioflags) ) hvm_wait_io(); @@ -498,7 +500,7 @@ void svm_do_resume(struct vcpu *v) if ( vpit->first_injected ) pickup_deactive_ticks(vpit); svm_set_tsc_shift(v, vpit); - + /* We can't resume the guest if we're waiting on I/O */ ASSERT(!test_bit(ARCH_HVM_IO_WAIT, &v->arch.hvm_vcpu.ioflags)); } diff --git a/xen/arch/x86/hvm/svm/x86_32/exits.S b/xen/arch/x86/hvm/svm/x86_32/exits.S index 33a0274078..7c6147269a 100644 --- a/xen/arch/x86/hvm/svm/x86_32/exits.S +++ b/xen/arch/x86/hvm/svm/x86_32/exits.S @@ -89,7 +89,7 @@ #define CLGI .byte 0x0F,0x01,0xDD #define DO_TSC_OFFSET 0 -#define DO_FPUSAVE 1 +#define DO_FPUSAVE 0 ENTRY(svm_asm_do_launch) sti diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c index 1068ae0c66..67e6f21835 100644 --- a/xen/arch/x86/hvm/vmx/vmx.c +++ b/xen/arch/x86/hvm/vmx/vmx.c @@ -615,8 +615,8 @@ static void vmx_do_no_device_fault(void) unsigned long cr0; struct vcpu *v = current; - clts(); setup_fpu(current); + __vm_clear_bit(EXCEPTION_BITMAP, EXCEPTION_BITMAP_NM); /* Disable TS in guest CR0 unless the guest wants the exception too. */ __vmread_vcpu(v, CR0_READ_SHADOW, &cr0); @@ -626,9 +626,6 @@ static void vmx_do_no_device_fault(void) cr0 &= ~X86_CR0_TS; __vmwrite(GUEST_CR0, cr0); } - - /* Xen itself doesn't need another exception. */ - __vm_clear_bit(EXCEPTION_BITMAP, EXCEPTION_BITMAP_NM); } /* Reserved bits: [31:15], [12:11], [9], [6], [2:1] */ @@ -1158,14 +1155,11 @@ static int vmx_set_cr0(unsigned long value) __vmread_vcpu(v, CR0_READ_SHADOW, &old_cr0); paging_enabled = (old_cr0 & X86_CR0_PE) && (old_cr0 & X86_CR0_PG); - /* - * Disable TS? Then we do so at the same time, and initialise FPU. - * This avoids needing another vmexit. - */ - if ( (old_cr0 & ~value & X86_CR0_TS) != 0 ) + /* TS cleared? Then initialise FPU now. */ + if ( !(value & X86_CR0_TS) ) { - clts(); setup_fpu(v); + __vm_clear_bit(EXCEPTION_BITMAP, EXCEPTION_BITMAP_NM); } __vmwrite(GUEST_CR0, value | X86_CR0_PE | X86_CR0_PG | X86_CR0_NE); @@ -1520,8 +1514,8 @@ static int vmx_cr_access(unsigned long exit_qualification, struct cpu_user_regs TRACE_VMEXIT(1,TYPE_CLTS); /* We initialise the FPU now, to avoid needing another vmexit. */ - clts(); - setup_fpu(current); + setup_fpu(v); + __vm_clear_bit(EXCEPTION_BITMAP, EXCEPTION_BITMAP_NM); __vmread_vcpu(v, GUEST_CR0, &value); value &= ~X86_CR0_TS; /* clear TS */ diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c index f5821ef195..3a1deb8535 100644 --- a/xen/arch/x86/traps.c +++ b/xen/arch/x86/traps.c @@ -1261,9 +1261,6 @@ asmlinkage int math_state_restore(struct cpu_user_regs *regs) struct trap_bounce *tb; struct trap_info *ti; - /* Prevent recursion. */ - clts(); - setup_fpu(current); if ( current->arch.guest_context.ctrlreg[0] & X86_CR0_TS ) diff --git a/xen/include/asm-x86/i387.h b/xen/include/asm-x86/i387.h index f9216605ed..fa9ba43e90 100644 --- a/xen/include/asm-x86/i387.h +++ b/xen/include/asm-x86/i387.h @@ -33,6 +33,7 @@ static inline void setup_fpu(struct vcpu *v) { if ( !test_and_set_bit(_VCPUF_fpu_dirtied, &v->vcpu_flags) ) { + clts(); if ( test_bit(_VCPUF_fpu_initialised, &v->vcpu_flags) ) restore_fpu(v); else -- 2.30.2